/*
 * (C) Copyright 2024 Quaming
 *
 * David G, Software Engineering, guzhihuan@quaming.com.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <watchdog.h>

#ifdef CONFIG_LOGBUFFER
#include <logbuff.h>
#endif

#include <rtc.h>

#include <environment.h>
#include <image.h>
#include <mapmem.h>

#include <u-boot/md5.h>
#include <u-boot/sha1.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <stdlib.h>
#include <stdbool.h>
#include <malloc.h>

DECLARE_GLOBAL_DATA_PTR;

#include <u-boot/md5.h>
#include <linux/time.h>
#include <image.h>
#include <autoboot.h>
#include <cli.h>
#include <console.h>
#include <serial.h>
#include <post.h>
#include <common.h>
#include <command.h>

#include <u-boot/crc.h>
#include "udisk_flash.h"
#include "util_flash.h"
//#include <android_bootloader_message.h>
#define UBOOT_DOWN_ADDR                 0x41000000
script_img_hdr global_script;

uint32_t script_get_header_size(const script_img_hdr *hdr)
{
	char *cmd_pos = strstr((char *)hdr, SCRIPT_CMD_START);
	if (cmd_pos != NULL) {
		return (uint32_t)cmd_pos;
	}
	return 0;
}
ulong script_get_header(const script_img_hdr *hdr)
{
      script_img_hdr *des_hdr = &global_script;
      char *cmd_pos = strstr((char *)hdr, SCRIPT_CMD_START);
      if (cmd_pos != NULL) {
              memcpy(des_hdr, hdr, ((ulong)cmd_pos > MAX_LINE_LENGTH) ? MAX_LINE_LENGTH:(ulong)cmd_pos);
		return 1;
      }
      return 0;
}

ulong script_get_data(const script_img_hdr *hdr)
{
       char *cmd_pos = strstr((char *)hdr, SCRIPT_CMD_START);

       if (cmd_pos != NULL) {
		   return ((ulong)cmd_pos + SCRIPT_CMD_START_SIZE);
       }
       return 0;
}
int script_check_header(const struct script_img_hdr *hdr)
{
	return memcmp(SCRIPT_CMD_MAGIC, hdr->magic, SCRIPT_CMD_MAGIC_SIZE);
}

char *get_key_value(const char *data, const char *key) {
    const char *newline = "\n";
  //  const char *delimiter = "=";
    char *line;
    char *key_value;
    char tmp[MAX_LINE_LENGTH];

    if(!data)
		return NULL;

    strcpy(tmp,data);

    // Tokenize data by newline
    line = strtok((char *)tmp, newline);

    while (line != NULL) {
        // Tokenize each line by "="
        key_value = strchr(line, '=');
        if (key_value != NULL) {
            // Split key and value
            *key_value = '\0'; // Terminate key
            key_value++; // Move past '='

            // Trim leading spaces from key
            while (*line == ' ') line++;

            // Trim leading spaces from value
            while (*key_value == ' ') key_value++;

            if (strcmp(key, line) == 0) {
                // Found key, return value
                return strdup(key_value);
            }
        }

        // Move to the next line
        line = strtok(NULL, newline);
    }

    // Key not found
    return NULL;
}


int script_check_config(const char *config,const char *key, const char *data, unsigned int length)
{
	char *value = get_key_value((char *)config, key);
	if (value != NULL) {
		if(!memcmp(value,data,length)){
			free(value); // Free the dynamically allocated memory
			return 0;
		}
		free(value); // Free the dynamically allocated memory
	}
	return -1;
}

int script_get_num(const char *config,const char *key, int *num)
{
	char *value = get_key_value((char *)config, key);
	if (value != NULL) {
		*num = (int)simple_strtoul(value, NULL, 10);
		free(value); // Free the dynamically allocated memory
		return 0;
	}
	return -1;
}
void parse_ethaddr(const char *addr, uchar *enetaddr) {
    char *end;
    int i;

    for (i = 0; i < 6; ++i) {
        enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0;
        if (addr) {
            // 如果当前字符是冒号，则跳过
            if (*end == ':')
                end++;
            addr = (*end) ? end : end;
        }
    }
}

void ethaddr2int( char *mac_string,unsigned int *lowint ,unsigned int *highint)
{
	unsigned char mac[6];
	parse_ethaddr(mac_string, mac);
	*highint = (mac[0] << 8) | mac[1];
	*lowint = (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5];
}


void int2ethaddr(uint32_t lowint, uint32_t highint, char *mac_string) 
{
    unsigned char mac[6];

    mac[0] = (highint >> 8) & 0xFF;
    mac[1] = highint & 0xFF;

    mac[2] = (lowint >> 24) & 0xFF;
    mac[3] = (lowint >> 16) & 0xFF;
    mac[4] = (lowint >> 8) & 0xFF;
    mac[5] = lowint & 0xFF;

    sprintf(mac_string, "%02x:%02x:%02x:%02x:%02x:%02x",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
int script_check_ethaddr(const char *config, char *ethaddr, unsigned int *lowint, unsigned int *highint)
{
	unsigned int lowint_start, highint_start;
	unsigned int lowint_end , highint_end;
	unsigned int lowint_current ,highint_current;

	/*get ethaddr start*/
	char *value = get_key_value((char *)config, SCRIPT_KEY_ETHADDR_START);
	if (!value)
		goto fail;

	ethaddr2int(value,&lowint_start,&highint_start);
	free(value); // Free the dynamically allocated memory
	/*get ethaddr poul end*/
	value = get_key_value((char *)config, SCRIPT_KEY_ETHADDR_END);
	if (!value)
		goto fail;

	ethaddr2int(value,&lowint_end,&highint_end);
	free(value); // Free the dynamically allocated memory

	/*get ethaddr from doc in udisk*/
	if (!ethaddr)
		goto fail;

	ethaddr2int(ethaddr,&lowint_current,&highint_current);
	printf("highint_current:0x%x,highint_start:0x%x,highint_end:0x%x\n",\
		highint_current,highint_start,highint_end);
	printf("lowint_current:0x%x,lowint_start:0x%x,lowint_end:0x%x\n",\
		lowint_current,lowint_start,lowint_end);

	if((highint_current==highint_start) && (highint_current==highint_end))
	{
		if((lowint_current>=lowint_start) && (lowint_current<=lowint_end))
		{
			*lowint 	= lowint_current;
			*highint 	= highint_current;
			int2ethaddr(lowint_current,highint_current,ethaddr);
			printf("script_check_ethaddr: ethaddr:%s\n",ethaddr);
			return 0;
		}
	}
fail:
	return 1;
}

#if defined(CONFIG_CMD_GREEN_LED)
#define LEDON           0
#define LEDOFF          1
#define LEDBLINK        2
int led_control(int status)
{
        int ret=0;
        char cmd[50];
        if(status == 0) {
                strcpy(cmd,"blinkled o");
        }
        if(status == 1) {
                strcpy(cmd,"blinkled f");
        }
        if(status == 2) {
                strcpy(cmd,"blinkled b 100 11");
        }
        ret = run_command(cmd, 0);
        return ret;

}
#endif

int flash_commands(void)
{
	int ret=0;
	char script_cmd[100]={0};
	memset((char *)UBOOT_DOWN_ADDR,0,0x2000);
	//detect the flag file as the condition to run script cmd or not
#if defined(CONFIG_SD_FLASH)
	sprintf(script_cmd,"fatload mmc 0:1 0x%lx %s",(long)UBOOT_DOWN_ADDR,script_file);
#elif defined(CONFIG_USB_FLASH)
	sprintf(script_cmd,"fatload usb 0:1 0x%lx %s",(long)UBOOT_DOWN_ADDR,script_file);
#endif
	ret = run_command(script_cmd, 0);
	printf("ret=%d,run command:%s\n",ret,script_cmd);
	if (ret){
		ret = -1;
		goto fail;
	}
	//start read script.ini file from udisk or sd card
#if !defined(CONFIG_FLASH_FW_ON_KEY_PRESS)
	memset(script_cmd,0,sizeof(script_cmd));
#if defined(CONFIG_SD_FLASH)
	sprintf(script_cmd,"fatload mmc 0:1 0x%lx %s",(long)UBOOT_DOWN_ADDR,isrun_script_file);
#elif defined(CONFIG_USB_FLASH)
	sprintf(script_cmd,"fatload usb 0:1 0x%lx %s",(long)UBOOT_DOWN_ADDR,isrun_script_file);
#endif
	ret = run_command(script_cmd, 0);
	printf("ret=%d,run command:%s\n",ret,script_cmd);
	if (!ret){
#if defined(CONFIG_SD_FLASH)
		printf("%s file already existed in sd card, so ignor script cmds\n",isrun_script_file);
#elif defined(CONFIG_USB_FLASH)
		printf("%s file already existed in U disk, so ignor script cmds\n",isrun_script_file);
#endif
		ret = -2;
		goto fail;
	}
#endif // !defined(CONFIG_FLASHING_ON_KEY_PRESS)
	//start run source cmd to run all commands in script.ini file
#if defined(CONFIG_CMD_GREEN_LED)
	led_control(LEDON);
#endif
	memset(script_cmd,0,sizeof(script_cmd));
	sprintf(script_cmd,"source 0x%lx",(long)UBOOT_DOWN_ADDR);
	ret = run_command(script_cmd, 0);
	printf("ret=%d,run command:%s\n",ret,script_cmd);
	if (ret){
		goto fail;
	}
	//
	if(!script_check_config(global_script.config,SCRIPT_KEY_DONE, "yes", 3)) {
		// create a new file as the flag to script cmd ok
		memset(script_cmd,0,sizeof(script_cmd));
#if defined(CONFIG_SD_FLASH)
		sprintf(script_cmd,"fatwrite mmc 0:1 0x%lx %s 7",(long)UBOOT_DOWN_ADDR,isrun_script_file);
#elif defined(CONFIG_USB_FLASH)
 		sprintf(script_cmd,"fatwrite usb 0:1 0x%lx %s 7",(long)UBOOT_DOWN_ADDR,isrun_script_file);
#endif
		ret = run_command(script_cmd, 0);
		printf("ret=%d,run command:%s\n",ret,script_cmd);
		if (ret){
			goto fail;
		}
	}

fail:
#if defined(CONFIG_CMD_GREEN_LED)
	if (ret > 0) {
		led_control(LEDOFF);
	}
#endif
	return ret;
}

int macid_commands(void)
{
	int ret=0;
	char script_cmd[100]={0};
	unsigned int lowint=0;
	unsigned int highint=0;
	char macid[MACID_FILE_SIZE+1]={0};
	char config[MACID_INI_MAX_SIZE+1]={0};
	int ethnum;
	char ethxaddr[10];
	int i;

	//read ethaddr from macid.dat file in U-disk
	memset(script_cmd,0,sizeof(script_cmd));
	sprintf(script_cmd,"mw 0x%lx 0 0x100",(long)UBOOT_DOWN_ADDR);
	ret = run_command(script_cmd, 0);
	memset(script_cmd,0,sizeof(script_cmd));
	sprintf(script_cmd,"fatload usb 0:1 0x%lx %s",(long)UBOOT_DOWN_ADDR,macid_dat);
	ret = run_command(script_cmd, 0);
	printf("ret=%d,run command:%s\n",ret,script_cmd);
	if (ret){
		printf("no %s file in U disk\n",macid_dat);
		ret = -1;
		goto fail;
	}
	memcpy(macid,(char *)UBOOT_DOWN_ADDR,MACID_FILE_SIZE);
	printf("macid:%s\n",macid);

	memset(script_cmd,0,sizeof(script_cmd));
	sprintf(script_cmd,"mw 0x%lx 0 0x100",(long)UBOOT_DOWN_ADDR);
	ret = run_command(script_cmd, 0);
	//start read macid_ini file from U-disk
	memset(script_cmd,0,sizeof(script_cmd));
	sprintf(script_cmd,"fatload usb 0:1 0x%lx %s",(long)UBOOT_DOWN_ADDR,macid_ini);
	ret = run_command(script_cmd, 0);
	printf("ret=%d,run command:%s\n",ret,script_cmd);
	if (ret){
		ret = -2;
		goto fail;
	}
	memcpy((char *)config,(char *)UBOOT_DOWN_ADDR,MACID_INI_MAX_SIZE);

	//check ethaddr and write it into env and files
	ret = script_check_ethaddr((char *)config,(char *)macid,&lowint,&highint);
	if (ret){
		ret = -3;
		goto fail;
	}
	script_get_num((char *)config,"ethnum",&ethnum);
	#if defined(CONFIG_CMD_GREEN_LED)
        //start macid writing, led on
        if(ethnum > 0)
		led_control(LEDON);
	#endif
	printf ("ethnum=%d\n",ethnum);
	for (i=0;i<ethnum;i++)
	{
		if(i==0) {
			strcpy(ethxaddr,"ethaddr");//eth0 mac id
		}
		else
			sprintf(ethxaddr,"eth%daddr", i);
		if(!script_check_config((char *)config,ethxaddr, "yes", 3))
		{
			// read ethxaddr from env
			memset(script_cmd,0,sizeof(script_cmd));
			sprintf(script_cmd,"printenv %s",ethxaddr);
			ret = run_command(script_cmd, 0);
			printf("ret=%d,run command:%s\n",ret,script_cmd);
			if (ret){
				// no ethxaddr yet, write the latest macid into macid_file
				lowint++;
				memset(script_cmd,0,sizeof(script_cmd));
				memset((char *)UBOOT_DOWN_ADDR,0,MACID_FILE_SIZE);
				int2ethaddr(lowint, highint, (char *)UBOOT_DOWN_ADDR);
				sprintf(script_cmd,"fatwrite usb 0:1 0x%lx %s %d",(long)UBOOT_DOWN_ADDR,macid_dat,MACID_FILE_SIZE);
				ret = run_command(script_cmd, 0);
				printf("ret=%d,run command:%s\n",ret,script_cmd);
				if (ret){
					goto fail;
				}
				// write ethxaddr into env
				memset(script_cmd,0,sizeof(script_cmd));
				sprintf(script_cmd,"setenv %s %s",ethxaddr,(char *)UBOOT_DOWN_ADDR);
				ret = run_command(script_cmd, 0);
				printf("ret=%d,run command:%s\n",ret,script_cmd);
				if (ret){
					goto fail;
				}
				// save env
				memset(script_cmd,0,sizeof(script_cmd));
				strcpy(script_cmd,"saveenv");
				ret = run_command(script_cmd, 0);
				printf("ret=%d,run command:%s\n",ret,script_cmd);
				if (ret){
					goto fail;
				}

			}
		}
	}

fail:
#if defined(CONFIG_CMD_GREEN_LED)
        if (ret > 0) {
                //macid writing fail, led off
                led_control(LEDOFF);
        }
#endif
	return ret;
}

#if defined(CONFIG_MOLCHIP_WATCHDOG)
extern void hw_watchdog_reset(void);
#endif
int bootup_commands(void)
{
	int ret=0;
	int flash_result=0;

#if defined(CONFIG_FLASH_FW_ON_KEY_PRESS)
	if (!flashing_fw_requested()) {
		printf("skip flashing fw...\n");
		return 0;
	}
#endif
#if defined(CONFIG_MOLCHIP_WATCHDOG)
        molchip_wdt_disable();
#endif
#if defined(CONFIG_USB_FLASH)
	ret = run_command("usb start", 0);
	printf("ret=%d run command:usb start\n",ret);
	if (ret){
		ret = -1;
		goto fail;
	}
	//udisk flashing
	printf("udisk flashing....\n");
#elif defined(CONFIG_SD_FLASH)
	printf("sd card flashing....\n");
#endif
	flash_result = flash_commands();
	if (flash_result < 0){
		//note: some cmd in source will return 1, but the cmd does run ok
		//like use "setenv ethaddr" cmd to clean macid, but it will case source cmd return 1
		//to save normal bootup time, if don't have udisk flash ini file, don't write macid ether.
		goto fail;
	}
	//macid writing
	printf("macid writing....\n");
	ret = macid_commands();
	if (ret){
		goto fail;
	}

fail:
#if defined(CONFIG_USB_FLASH)
	printf("run command:usb stop,ret=%d\n",ret);
	run_command("usb stop", 0);
#endif

	if(!script_check_config((char *)global_script.config,SCRIPT_KEY_REBOOT, "yes", 3)) {
		printf ("system will reboot\n");
		mdelay(100);
		run_command("reset", 0);
	}
#if defined(CONFIG_CMD_GREEN_LED)
	if (!ret && !flash_result) {
		//both flash and macid writing pass, led blinding and block system
		//note: this LEDBLIND cmd will block the uboot system forever, till power off
		led_control(LEDBLINK);
	}
	if((ret > 0) || flash_result > 0){
		//flash or macid writing fail, led closed and block system
		led_control(LEDOFF);
		printf("usb image flash failed or macid write failed!!!\n");
		while(1);
	}
#endif
#if defined(CONFIG_MOLCHIP_WATCHDOG)
        hw_watchdog_reset();
#endif
	return ret;
}

